using System;
using System.Management;
using System.Globalization;

using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace CSharpDistilled.Diagnostics
{
    /// <summary>
    /// Represents a physical memory module.
    /// </summary>
    public class MemoryModule : Device
    {
        #region MemoryModule Private Fields

        private long m_capacity;
        private int m_speed;

        private const string SystemRom = "SYSTEM ROM";

        #endregion

        #region private MemoryModule()

        /// <summary>
        /// Initializes a new instance of the <see cref="T:MemoryModule"/> class.    
        /// </summary>
        private MemoryModule()
        {

        }

        #endregion

        #region public static IList<MemoryModule> GetMemoryModules()

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.IList`1"/> containin a <see cref="T:MemoryModule"/> instance for each physical memory module.
        /// </summary>
        /// <remarks>
        /// The <see cref="M:MemoryModule.GetMemoryModules"/> method is implemented as a method and not a property because the list of memory modules will take considerably
        /// longer to retrieve than a simple field. Static code analysis warning CA1024 is therefore suppressed after consulting the .NET Framework Design Guidelines written by
        /// Brad Abrams and Krzysztof Cwalina which is published by Addison Wesley.
        /// </remarks>
        /// <returns>An <see cref="T:System.Collections.Generic.IList`1"/> containin a <see cref="T:MemoryModule"/> instance for each physical memory module.</returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public static IList<MemoryModule> GetMemoryModules()
        {
            try
            {
                List<MemoryModule> modules = new List<MemoryModule>();

                ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\CIMV2", "SELECT * FROM Win32_PhysicalMemory");

                foreach (ManagementObject managementObject in searcher.Get())
                {
                    MemoryModule module = new MemoryModule();

                    module.Name = managementObject["Name"] as string;
                    module.Description = managementObject["Description"] as string;
                    module.DeviceId = managementObject["DeviceLocator"] as string;

                    if (string.Equals(module.DeviceId, SystemRom))
                    {
                        // ignore System ROM memory.
                        continue;
                    }

                    module.Capacity = Convert.ToInt64(managementObject["Capacity"], CultureInfo.CurrentCulture);
                    module.Speed = Convert.ToInt32(managementObject["Speed"], CultureInfo.CurrentCulture);

                    modules.Add(module);
                }

                return new ReadOnlyCollection<MemoryModule>(modules);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
                Trace.WriteLine(ex.StackTrace);

                throw;
            }
        } 

        #endregion

        #region public long Capacity

        /// <summary>
        /// Gets or sets the amount of memory in bytes for the memory module.
        /// </summary>
        /// <value>The amount of memory in bytes for the memory module.</value>
        public long Capacity
        {
            get
            {
                return m_capacity;
            }
            private set
            {
                m_capacity = value;
            }
        }

        #endregion

        #region public int Speed

        /// <summary>
        /// Gets or sets the speed of the memory module.
        /// </summary>
        /// <value>The speed of the memory module.</value>
        public int Speed
        {
            get
            {
                return m_speed;
            }
            private set
            {
                m_speed = value;
            }
        }

        #endregion
    }
}
